package com.sunline.poc.oozie.aop;

import com.sunline.poc.oozie.ApplicationProperties;
import com.sunline.poc.oozie.config.LoginUserContext;
import com.sunline.poc.oozie.service.UserService;
import com.sunline.poc.oozie.vo.UserInfoVO;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.security.PrivilegedAction;

/**
 * 权限认证有2种方式
 * 1.权限交给服务来做，CDH给服务一个CDH用户，设定这个CDH用户的资源池上限，服务决定每一个具体的用户能分配的资源和对应的权限。
 * 2.权限交给CDH来做，CDH给每一个用户分配一个资源池。
 *
 * 使用第一种的特点
 * 真是用户对CDH无法感知，用户也无法绕过服务去访问CDH，CDH是绝对安全的。
 *
 * 使用第二种的特点
 * 1.CDH给每个用户都分配了用户，用户可以绕过服务，直接访问CDH，调用对应的资源，有安全风险。
 * 2.服务每次提交job都需要进行一次认证，因为认证属于全局认证，可能会有线程安全的问题。
 *
 * 本例使用第二种
 */
@Aspect
@Component
@Order(998)
public class OzServiceSecurityAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ApplicationProperties applicationProperties;

    @Pointcut("execution(* com.sunline.poc.oozie.service.impl.OozieServiceImpl.*(..)) ")
    public void point() {
    }

    @Around("point()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        UserInfoVO loginUser = LoginUserContext.getLoginUser();

        //kerberos安全认证
        System.setProperty("java.security.krb5.conf", applicationProperties.getKrb5FilePath());
        System.setProperty("sun.security.krb5.debug", "true");
        Configuration conf = new Configuration();
        conf.set("hadoop.security.authentication","kerberos");
        conf.set("oozie.security.authentication","kerberos");
        UserGroupInformation.setConfiguration(conf);
        UserGroupInformation ugi = null;
        try {
            ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(loginUser.getKerberosPrincipal(), loginUser.getKerberosKeytabFilePath());
        }catch (Exception e){
            e.printStackTrace();
            throw new Exception("Kerberos login failed!");
        }

        logger.info("kerberos login success");

       Object result =  ugi.doAs(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                //执行
                try {
                    return pjp.proceed();
                } catch (Throwable throwable) {
                    return  throwable;
                }
            }
        });

       if (result instanceof  Throwable){
           throw  new RuntimeException((Throwable) result);
       }
        return  result;
    }
}
